package com.jing21.learn.elasticsearch;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jing21.learn.common.learn.exception.LogicException;
import com.jing21.learn.elasticsearch.intf.annotaion.DisableSourse;
import com.jing21.learn.elasticsearch.intf.annotaion.ESField;
import com.jing21.learn.elasticsearch.intf.query.BaseIndexQuery;
import com.jing21.learn.elasticsearch.intf.query.FriendCricleQuery;

import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 构造es查询json
 *
 * @author zhengjing
 * @version 1.0
 * @date: 2017/10/20 14:40
 */
public class BuilderQueryString {

    private static Map<String, com.jing21.learn.elasticsearch.EsQueryConfig> configs = new HashMap<>();

    private List sorts = new ArrayList();

    private List<String> sources = new ArrayList<>();

    private Map<String, Object> bool = new HashMap<>();

    /**
     * 构造查询字符串
     *
     * @param baseIndexQuery 查询对象
     * @return
     */
    public <T extends BaseIndexQuery> String build(T baseIndexQuery) throws LogicException {
        return build(baseIndexQuery, null);
    }

    /**
     * 构造查询字符串
     *
     * @param indexQuery 查询对象
     * @param config     自定义配置（会替代默认的配置）
     * @return
     */
    public <T extends BaseIndexQuery> String build(T indexQuery, com.jing21.learn.elasticsearch.EsQueryConfig config) throws LogicException {

        com.jing21.learn.elasticsearch.EsQueryConfig defaultConfig = buildQueryConfig(indexQuery);

        JSONObject indexMap = (JSONObject) JSON.toJSON(indexQuery);
        //构造字段值
        defaultConfig.getFieldConfigMap().forEach((key, value) -> {

            //如果配置不为空并且存在当前值
            if (config != null && config.getFieldConfigMap().containsKey(key)) {
                value = config.getFieldConfigMap().get(key);
            }

            final com.jing21.learn.elasticsearch.EsQueryFieldConfig fieldConfig = value;

            buildCond(fieldConfig, indexMap.get(key));

        });

        //构造显示字段 排序字段等信息
        defaultConfig.getStaticConfig().forEach((key, value) -> {
            //如果配置不为空并且存在当前值
            if (config != null && config.getStaticConfig().containsKey(key)) {
                value = config.getStaticConfig().get(key);
            }

            final com.jing21.learn.elasticsearch.EsQueryFieldConfig fieldConfig = value;

            buildSort(fieldConfig);
            if ((config != null && config.isSourse()) || defaultConfig.isSourse()) {
                buildSource(fieldConfig);
            }

        });

        Map<String, Object> json = new HashMap<>();
        if (!sorts.isEmpty())
            json.put("sort", sorts);

        if (!sources.isEmpty())
            json.put("_source", sources);

        Map<String, Object> query = new HashMap<>();
        bool.forEach((key, value) -> {
            List conds = (List) value;
            if (conds.size() == 1) {
                bool.put(key, conds.get(0));
            }
        });
        json.put("query", query);
        query.put("bool", bool);

        return JSON.toJSONString(json);
    }


    /**
     * 获取查询配置
     *
     * @return
     */
    private synchronized <T extends BaseIndexQuery> com.jing21.learn.elasticsearch.EsQueryConfig buildQueryConfig(T baseIndexPo) throws
            LogicException {
        Class<? extends BaseIndexQuery> indexClass = baseIndexPo.getClass();

        if (!configs.containsKey(indexClass.getSimpleName())) {
            com.jing21.learn.elasticsearch.EsQueryConfig queryConfig = new com.jing21.learn.elasticsearch.EsQueryConfig();

            queryConfig.setEsIndexTypeEnum(baseIndexPo.getEsIndexType());
            queryConfig.setFieldConfigMap(readFieldConfig(indexClass));
            if (baseIndexPo.getSourceClass().getAnnotation(DisableSourse.class) != null) {
                queryConfig.setSourse(false);
            }
            queryConfig.setStaticConfig(readFieldConfig(baseIndexPo.getSourceClass()));
            configs.put(indexClass.getSimpleName(), queryConfig);
        }

        return configs.get(indexClass.getSimpleName());
    }

    /**
     * 读取配置
     *
     * @param classic
     * @return
     */
    private Map<String, com.jing21.learn.elasticsearch.EsQueryFieldConfig> readFieldConfig(Class<?> classic) {

        Map<String, com.jing21.learn.elasticsearch.EsQueryFieldConfig> fieldConfigs = Stream.of(classic.getDeclaredFields())
                //静态变量排除
                .filter(field -> !Modifier.isStatic(field.getModifiers()))
                //转换成查询配置
                .map(field -> com.jing21.learn.elasticsearch.EsQueryFieldConfig.buildByAnnotation(field.getName(), field.getAnnotation(ESField
                        .class)))
                .collect(Collectors.toMap(com.jing21.learn.elasticsearch.EsQueryFieldConfig::getFieldName, Function.identity()));
        return fieldConfigs;

    }

    /**
     * 构造排序
     */
    private void buildSort(com.jing21.learn.elasticsearch.EsQueryFieldConfig config) {
        if (config.isSort()) {
            sorts.add(Collections.singletonMap(config.getFieldName(),
                    Collections.singletonMap("order", config.getSortType())));

        }
    }

    /**
     * 构造显示字段
     */
    private void buildSource(com.jing21.learn.elasticsearch.EsQueryFieldConfig config) {
        if (config.isShow()) {
            sources.add(config.getEsFieldName());
        }
    }

    /**
     * 构造子查询
     */
    private void buildCond(com.jing21.learn.elasticsearch.EsQueryFieldConfig config, Object value) {

        if (Objects.nonNull(value)) {

            List<Map<String, Object>> group = findGroup(config.getGroup());
            Map<String, Object> map = new HashMap<>();
            map.put(config.getQueryType(), Collections.singletonMap(config.getEsFieldName(), config
                    .getValuePolicy().translateValue(value)));
            group.add(map);
        }
    }

    /**
     * 获取子查询组
     *
     * @param group
     */
    private List<Map<String, Object>> findGroup(String group) {
        if (!bool.containsKey(group)) {
            bool.put(group, new ArrayList<>());
        }
        return (List<Map<String, Object>>) bool.get(group);
    }

    public static void main(String[] args) {
        BuilderQueryString queryString = new BuilderQueryString();
        FriendCricleQuery query = new FriendCricleQuery();
        query.setLastid(new BigInteger("13"));
        query.setThemealias("themealias");
        query.setContent("content");
        System.err.println(queryString.build(query));
    }
}
